/*
 * @(#)TimeControllerImpl.java   09/10/11
 * 
 * Copyright (C) 2009 Ecole des Mines d'Ales
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Contact: yulinz88@gmail.com
 */



package fr.ema.arch3d.ref.interceptors.impls;

import fr.ema.arch3d.ref.controllers.interfaces.ConnectorController;
import fr.ema.arch3d.ref.interceptors.interfaces.TimeController;

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.NoSuchInterfaceException;
import org.objectweb.fractal.julia.loader.Initializable;
import org.objectweb.fractal.julia.loader.Tree;

import java.lang.reflect.InvocationTargetException;

//~--- classes ----------------------------------------------------------------

/**
 * Class description
 *
 *
 * @version        1.0, 09/09/13
 * @author         Huaxi (Yulin) ZHANG
 */
public class TimeControllerImpl implements TimeController, Initializable {

    /** Field description */
    private int calls;

    /** Field description */
    private int defineTestTimesT;

    /** Field description */
    private int localTestTimesT;

    /** Field description */
    private int pPre, pPost, rPre, rPost;

    /** Field description */
    private boolean provided, onlineR;

    /** Field description */
    private int reads;

    /** Field description */
    private Object[][] resultT;

    /** Field description */
    private long startOld, finishOld, startNew, finishNew;

    /** Field description */
    private int success;

    /** Field description */
    private Component targetT;

    /** Field description */
    private boolean test;

    /** Field description */
    private int writes;

    /**
     * Method description
     *
     * @param args  
     */
    public void initialize(final Tree args) {
        String s = args.getSubTree(0).toString();
    }

    /**
     * The method to record the result with method name and time
     *
     * @param method
     * @throws NoSuchInterfaceException
     */
    public void recordTime(String method) throws NoSuchInterfaceException {
        ConnectorController cc = (ConnectorController) this.targetT.getFcInterface("connector-controller");

        resultT[this.localTestTimesT][0] = method;
        resultT[this.localTestTimesT][1] = new Long(this.startOld);
        resultT[this.localTestTimesT][2] = new Long(this.finishOld);
        resultT[this.localTestTimesT][3] = new Long(this.startNew);
        resultT[this.localTestTimesT][4] = new Long(this.finishNew);
        this.localTestTimesT++;

        if (this.localTestTimesT == this.defineTestTimesT) {
            test                 = false;
            this.localTestTimesT = 0;

            // this.defineTestTimesT=0;
            cc.finishTestT(resultT);
        }
    }

    /**
     * Method description
     *
     * @param defineTestTimesT  
     * @param off  
     */
    public void beginTestP(int defineTestTimesT, boolean off) {
        this.defineTestTimesT = defineTestTimesT;
        resultT               = new Object[defineTestTimesT][5];
        this.localTestTimesT  = 0;
        test                  = true;
        pPre                  = 0;

        if (off) {
            pPost = 0;
        } else {
            pPost = -1;
        }

        provided = true;
    }

    /**
     * Method description
     *
     * @param defineTestTimesT  
     * @param off  
     */
    public void beginTestR(int defineTestTimesT, boolean off) {
        if (off) {
            this.defineTestTimesT = defineTestTimesT;
            resultT               = new Object[defineTestTimesT][5];
            this.localTestTimesT  = 0;
            provided              = false;
            rPre                  = 0;
            rPost                 = 0;
            test                  = true;
        } else {
            this.defineTestTimesT = defineTestTimesT;
            rPost                 = 2;
            this.onlineR          = true;
        }
    }

    /**
     * Method description
     *
     * @param itf  
     * @param target  
     * @param method  
     * @throws IllegalAccessException  
     * @throws IllegalArgumentException  
     * @throws InstantiationException  
     * @throws InvocationTargetException  
     * @throws NoSuchFieldException  
     * @throws NoSuchInterfaceException  
     * @throws SecurityException  
     */
    public void preMethod(final String itf, final Object target, final String method)
            throws NoSuchInterfaceException, SecurityException, NoSuchFieldException, IllegalAccessException,
                   InstantiationException, IllegalArgumentException, InvocationTargetException {
        if (onlineR) {

            // System.out.println("eeeeeeeeeeeeeeeee"+this.defineTestTimesT);
            resultT              = new Object[defineTestTimesT][5];
            this.localTestTimesT = 0;
            provided             = false;
            rPre                 = 0;
            test                 = true;
            onlineR              = false;
        }

        if (test) {
            synchronized (this) {
                if (provided) {
                    pPre++;

                    // System.out.println("pre="+pPre);
                    this.targetT = (Component) target;

                    if (this.pPre == 2) {
                        this.startOld = System.currentTimeMillis();
                    } else if (this.pPre == 3) {
                        this.startNew = System.currentTimeMillis();
                    }
                } else {
                    rPre++;

                    // System.out.println("pre="+rPre);
                    this.targetT = (Component) target;

                    if (this.rPre == 1) {
                        this.startOld = System.currentTimeMillis();
                    } else if (this.rPre == 3) {
                        this.startNew = System.currentTimeMillis();
                        rPre          = 0;
                    }
                }
            }
        }
    }

    /**
     * Method description
     *
     * @param itf  
     * @param target  
     * @param method  
     * @throws NoSuchInterfaceException  
     */
    public void postMethod(final String itf, final Object target, final String method) throws NoSuchInterfaceException {
        if (test) {
            synchronized (this) {
                if (provided) {
                    pPost++;

                    // System.out.println("yyyyyyyyypost="+pPost+test);
                    ++success;

                    if (pPost == 1) {
                        this.finishOld = System.currentTimeMillis();
                    } else if (pPost == 2) {
                        this.finishNew = System.currentTimeMillis();
                        recordTime(method);
                    } else if (pPost == 3) {

                        // System.out.println("oooooooooooooo");
                        pPost = 0;
                        pPre  = 0;
                    }
                } else {
                    rPost++;

                    // System.out.println("post="+rPost);
                    if (rPost == 2) {
                        this.finishOld = System.currentTimeMillis();
                    }

                    if (rPost == 3) {
                        this.finishNew = System.currentTimeMillis();
                        recordTime(method);
                        rPost = 0;
                    }
                }
            }
        }
    }

    /**
     * Method description
     *
     * @param field  
     * @param owner  
     */
    public void getField(final String field, final String owner) {
        ++reads;

        // System.out.println("getfield=="+ field+" "+" owner=" +owner);
    }

    /**
     * Method description
     *
     * @param field  
     * @param owner  
     */
    public void putField(final String field, final String owner) {

        // if(name=="print"){mv.visitFieldInsn(GETFIELD,owner,"offline","I");
        // mv.visitFieldInsn(PUTFIELD, owner, "Init", "I");}
        // if(field=="init")
        // System.out.println("putfield== "+field +" owner=" +owner);
        ++writes;
    }
}
